#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
专门用于调试Celery trace模块中_loc变量的脚本
聚焦于解决"not enough values to unpack (expected 3, got 0)"错误
"""

import os
import sys
import time
import traceback
import logging
from datetime import datetime

# 设置日志配置
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('debug_loc_variable.log', encoding='utf-8'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger('loc_variable_debugger')

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# 设置Django环境
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'baojing.settings')
import django
django.setup()

# 导入Celery相关模块
try:
    from celery.app import trace
    from baojing import celery as celery_app
    # 获取Celery版本的正确方式
    import celery
    celery_version = celery.__version__
    logger.info(f"已成功导入Celery模块，版本: {celery_version}")
except Exception as e:
    logger.error(f"导入Celery模块失败: {str(e)}")
    logger.error(traceback.format_exc())
    sys.exit(1)

class LocVariableDebugger:
    """专门用于调试_loc变量的调试器"""
    
    def __init__(self):
        self.original_fast_trace_task = None
        self.loc_value_history = []
        self.error_count = 0
        self.max_errors = 5  # 最多记录5个错误后退出
    
    def monkey_patch_trace_module(self):
        """对celery.app.trace模块进行猴子补丁，监控_loc变量"""
        try:
            # 保存原始函数
            self.original_fast_trace_task = trace.fast_trace_task
            
            # 创建包装函数
            def patched_fast_trace_task(task_id, args, kwargs, request=None):
                # 在执行前检查_loc变量
                self._check_loc_variable("执行前")
                
                try:
                    # 执行原始函数
                    result = self.original_fast_trace_task(task_id, args, kwargs, request)
                    return result
                except ValueError as e:
                    if "not enough values to unpack" in str(e):
                        self.error_count += 1
                        logger.error(f"====== 捕获到目标解包错误 (计数: {self.error_count}) ======")
                        # 详细记录_loc变量状态
                        self._check_loc_variable("错误发生时")
                        logger.error(f"任务ID: {task_id}")
                        logger.error(f"参数: args={args}, kwargs={kwargs}")
                        logger.error(f"请求对象: {request}")
                        logger.error(f"错误详情: {str(e)}")
                        logger.error(traceback.format_exc())
                        
                        if self.error_count >= self.max_errors:
                            logger.info("已达到最大错误记录数，正在退出...")
                            self.restore_original_functions()
                            sys.exit(1)
                    raise
                finally:
                    # 在执行后检查_loc变量
                    self._check_loc_variable("执行后")
            
            # 应用补丁
            trace.fast_trace_task = patched_fast_trace_task
            logger.info("已成功为celery.app.trace.fast_trace_task应用猴子补丁")
            
            # 检查trace模块的属性，查找_loc变量的定义位置
            self._inspect_trace_module()
            
        except Exception as e:
            logger.error(f"应用猴子补丁时出错: {str(e)}")
            logger.error(traceback.format_exc())
    
    def _check_loc_variable(self, context):
        """检查并记录_loc变量的状态"""
        try:
            if hasattr(trace, '_loc'):
                loc_value = trace._loc
                timestamp = datetime.now().isoformat()
                loc_info = {
                    'timestamp': timestamp,
                    'context': context,
                    'value': loc_value,
                    'type': type(loc_value).__name__,
                    'length': len(loc_value) if isinstance(loc_value, (list, tuple)) else None
                }
                self.loc_value_history.append(loc_info)
                
                # 记录_loc变量的详细信息
                if loc_value is None:
                    logger.warning(f"[{context}] trace._loc的值为None")
                elif isinstance(loc_value, (list, tuple)):
                    if len(loc_value) != 3:
                        logger.warning(f"[{context}] trace._loc的长度不为3: {len(loc_value)}, 值: {loc_value}")
                    else:
                        logger.debug(f"[{context}] trace._loc的值: {loc_value}")
                else:
                    logger.warning(f"[{context}] trace._loc不是列表或元组: {type(loc_value)}, 值: {loc_value}")
            else:
                logger.warning(f"[{context}] trace模块中未找到_loc变量")
        except Exception as e:
            logger.error(f"检查_loc变量时出错: {str(e)}")
    
    def _inspect_trace_module(self):
        """检查trace模块的属性，查找_loc变量的定义位置"""
        try:
            logger.info("正在检查trace模块的属性...")
            
            # 检查模块级变量
            module_vars = [var for var in dir(trace) if not var.startswith('__')]
            logger.debug(f"trace模块的公共变量: {module_vars}")
            
            # 查找与_loc相关的变量
            loc_related_vars = [var for var in module_vars if 'loc' in var.lower()]
            logger.debug(f"与loc相关的变量: {loc_related_vars}")
            
            # 检查trace模块的源代码
            if hasattr(trace, '__file__'):
                trace_file = trace.__file__
                logger.info(f"trace模块文件路径: {trace_file}")
                
                # 尝试读取模块文件内容，查找_loc的定义
                try:
                    with open(trace_file, 'r', encoding='utf-8') as f:
                        content = f.read()
                        loc_occurrences = content.count('_loc')
                        logger.info(f"在trace模块文件中找到_loc的{loc_occurrences}处引用")
                        
                        # 查找_loc的定义上下文
                        lines = content.split('\n')
                        for i, line in enumerate(lines):
                            if '_loc' in line and ('=' in line or ':' in line):
                                context_lines = lines[max(0, i-3):min(len(lines), i+4)]
                                logger.debug(f"_loc定义上下文 (行{i+1}):\n" + '\n'.join(context_lines))
                                break
                except Exception as e:
                    logger.error(f"读取trace模块文件失败: {str(e)}")
            
        except Exception as e:
            logger.error(f"检查trace模块时出错: {str(e)}")
    
    def test_with_simple_task(self):
        """使用简单任务测试_loc变量行为"""
        try:
            from tasks.task_scheduler import check_and_schedule_tasks
            
            logger.info("=== 开始使用简单任务测试 ===")
            
            # 测试1: 直接调用函数
            logger.info("测试1: 直接调用check_and_schedule_tasks函数")
            result = check_and_schedule_tasks()
            logger.info(f"直接调用结果: {result}")
            
            # 测试2: 使用delay异步执行
            logger.info("测试2: 使用delay异步执行任务")
            result = check_and_schedule_tasks.delay()
            logger.info(f"异步任务ID: {result.id}")
            
            # 等待任务完成
            time.sleep(5)
            if result.ready():
                try:
                    task_result = result.get(timeout=1)
                    logger.info(f"异步任务结果: {task_result}")
                except Exception as e:
                    logger.error(f"获取异步任务结果时出错: {str(e)}")
            else:
                logger.warning("异步任务尚未完成")
            
            logger.info("=== 简单任务测试完成 ===")
        except Exception as e:
            logger.error(f"执行简单任务测试时出错: {str(e)}")
            logger.error(traceback.format_exc())
    
    def restore_original_functions(self):
        """恢复原始函数"""
        try:
            if self.original_fast_trace_task:
                trace.fast_trace_task = self.original_fast_trace_task
                logger.info("已恢复原始的fast_trace_task函数")
        except Exception as e:
            logger.error(f"恢复原始函数时出错: {str(e)}")
    
    def generate_report(self):
        """生成_loc变量状态报告"""
        logger.info("=== _loc变量状态报告 ===")
        logger.info(f"总共记录了{len(self.loc_value_history)}条_loc变量状态")
        
        # 统计不同类型的_loc值
        value_types = {}
        for info in self.loc_value_history:
            value_type = info['type']
            value_types[value_type] = value_types.get(value_type, 0) + 1
        
        for value_type, count in value_types.items():
            logger.info(f"类型 {value_type}: {count} 次")
        
        # 查找异常的_loc值
        abnormal_values = [info for info in self.loc_value_history \
                          if info['value'] is None or \
                          (isinstance(info['value'], (list, tuple)) and len(info['value']) != 3)]
        
        if abnormal_values:
            logger.warning(f"发现{len(abnormal_values)}个异常的_loc值")
            for info in abnormal_values[:3]:  # 只显示前3个
                logger.warning(f"  - [{info['timestamp']}] {info['context']}: {info['value']}")
        else:
            logger.info("未发现异常的_loc值")
        
        logger.info("=== 报告生成完成 ===")

# 主函数
def main():
    """主函数"""
    logger.info("=== 开始调试_loc变量 ===")
    
    debugger = LocVariableDebugger()
    
    try:
        # 应用猴子补丁
        debugger.monkey_patch_trace_module()
        
        # 执行测试任务
        debugger.test_with_simple_task()
        
        # 等待一段时间，观察是否有错误发生
        logger.info("等待30秒观察是否有其他任务执行...")
        time.sleep(30)
        
        # 生成报告
        debugger.generate_report()
        
    except KeyboardInterrupt:
        logger.info("调试被用户中断")
    except Exception as e:
        logger.error(f"调试过程中发生错误: {str(e)}")
        logger.error(traceback.format_exc())
    finally:
        # 恢复原始函数
        debugger.restore_original_functions()
        logger.info("=== 调试_loc变量结束 ===")

if __name__ == '__main__':
    main()